import os
import uuid
import shutil
from utils.logger import get_logger
from datetime import datetime, timedelta
from config import Config
from video_processor import VideoBackgroundReplacer

logger = get_logger(__name__)


class FileManager:
    """文件管理器"""

    def __init__(self):
        # 确保文件夹存在
        Config.init_folders()
        # 确保分片临时目录存在
        chunks_dir = os.path.join(Config.TEMP_FOLDER, 'chunks')
        os.makedirs(chunks_dir, exist_ok=True)

    def save_uploaded_file(self, file, file_type):
        """保存上传的文件（传统方式）"""
        try:
            file_id = str(uuid.uuid4())
            original_name = file.filename

            # 获取文件扩展名
            file_ext = os.path.splitext(original_name)[1]

            # 检查文件类型
            if not self._is_allowed_file(file_ext, file_type):
                return None, "文件类型不支持"

            # 生成保存路径
            saved_filename = f"{file_id}{file_ext}"
            file_path = os.path.join(Config.LIBRARY_FOLDER, file_type, saved_filename)

            # 确保目录存在
            os.makedirs(os.path.dirname(file_path), exist_ok=True)

            # 保存文件
            file.save(file_path)

            # 获取文件大小
            file_size = os.path.getsize(file_path)

            # 生成预览
            preview_path = self._generate_preview(file_id, file_path, file_ext)

            return {
                'file_id': file_id,
                'original_name': original_name,
                'file_type': file_type,
                'file_path': file_path,
                'preview_path': preview_path,
                'file_size': file_size
            }, None

        except Exception as e:
            logger.error(f"保存文件失败: {e}")
            return None, f"保存文件失败: {str(e)}"

    def process_chunked_file(self, file_id, original_name, file_type, file_path, file_size):
        """处理分片上传完成后的文件"""
        try:
            # 获取文件扩展名
            file_ext = os.path.splitext(original_name)[1]

            # 验证文件
            if not os.path.exists(file_path):
                return None, "文件不存在"

            # 验证文件大小
            actual_size = os.path.getsize(file_path)
            if actual_size != file_size:
                return None, f"文件大小不匹配: 期望 {file_size}, 实际 {actual_size}"

            # 生成预览
            preview_path = self._generate_preview(file_id, file_path, file_ext)

            logger.info(f"分片文件处理完成: {file_id}, 原名: {original_name}, 大小: {file_size}")

            return {
                'file_id': file_id,
                'original_name': original_name,
                'file_type': file_type,
                'file_path': file_path,
                'preview_path': preview_path,
                'file_size': file_size
            }, None

        except Exception as e:
            logger.error(f"处理分片文件失败: {e}")
            return None, f"处理分片文件失败: {str(e)}"

    def _is_allowed_file(self, file_ext, file_type):
        """检查文件类型是否允许"""
        file_ext = file_ext.lower()

        if file_type == 'human':
            # 数字人文件只允许视频
            return file_ext in Config.ALLOWED_VIDEO_EXTENSIONS
        elif file_type == 'background':
            # 背景文件允许图片和视频
            return (file_ext in Config.ALLOWED_IMAGE_EXTENSIONS or
                    file_ext in Config.ALLOWED_VIDEO_EXTENSIONS)

        return False

    def _generate_preview(self, file_id, file_path, file_ext):
        """生成文件预览"""
        try:
            preview_path = None

            if file_ext.lower() in Config.ALLOWED_VIDEO_EXTENSIONS:
                # 视频生成缩略图
                preview_filename = f"{file_id}_thumb.jpg"
                preview_path = os.path.join(Config.PREVIEW_FOLDER, preview_filename)

                # 使用视频处理器生成缩略图
                replacer = VideoBackgroundReplacer(file_id)
                if replacer.generate_thumbnail(file_path, preview_path):
                    return preview_path
                else:
                    return None
            else:
                # 图片直接复制
                preview_filename = f"{file_id}_preview{file_ext}"
                preview_path = os.path.join(Config.PREVIEW_FOLDER, preview_filename)
                shutil.copy2(file_path, preview_path)
                return preview_path

        except Exception as e:
            logger.error(f"生成预览失败: {e}")
            return None

    def generate_video_preview(self, video_path):
        """为视频生成预览缩略图（用于分片上传）"""
        try:
            file_id = str(uuid.uuid4())
            preview_filename = f"{file_id}_thumb.jpg"
            preview_path = os.path.join(Config.PREVIEW_FOLDER, preview_filename)

            replacer = VideoBackgroundReplacer(file_id)
            if replacer.generate_thumbnail(video_path, preview_path):
                return preview_path
            return None
        except Exception as e:
            logger.error(f"生成视频预览失败: {e}")
            return None

    def delete_file(self, file_path):
        """删除文件"""
        try:
            if os.path.exists(file_path):
                os.remove(file_path)
                logger.info(f"已删除文件: {file_path}")
                return True
        except Exception as e:
            logger.error(f"删除文件失败 {file_path}: {e}")
        return False

    def get_file_info(self, file_path):
        """获取文件信息"""
        try:
            if not os.path.exists(file_path):
                return None

            stat = os.stat(file_path)
            return {
                'size': stat.st_size,
                'created_time': datetime.fromtimestamp(stat.st_ctime),
                'modified_time': datetime.fromtimestamp(stat.st_mtime),
                'exists': True
            }
        except Exception as e:
            logger.error(f"获取文件信息失败: {e}")
            return None

    def cleanup_chunks(self, hours=2):
        """清理分片临时文件"""
        try:
            chunks_dir = os.path.join(Config.TEMP_FOLDER, 'chunks')
            if not os.path.exists(chunks_dir):
                return 0

            cutoff_time = datetime.now() - timedelta(hours=hours)
            cleaned_count = 0

            for item in os.listdir(chunks_dir):
                item_path = os.path.join(chunks_dir, item)
                if os.path.isdir(item_path):
                    try:
                        item_time = datetime.fromtimestamp(os.path.getctime(item_path))
                        if item_time < cutoff_time:
                            shutil.rmtree(item_path, ignore_errors=True)
                            cleaned_count += 1
                            logger.info(f"已清理过期分片目录: {item_path}")
                    except Exception as e:
                        logger.error(f"清理分片目录失败 {item_path}: {e}")

            logger.info(f"分片清理完成，清理了 {cleaned_count} 个目录")
            return cleaned_count

        except Exception as e:
            logger.error(f"清理分片文件失败: {e}")
            return 0


class PreviewManager:
    """预览管理器"""

    def __init__(self):
        self.active_previews = {}

    def create_preview(self, preview_id, human_file_path, bg_file_path, params):
        """创建预览"""
        try:
            # 生成预览文件名
            preview_filename = f"preview_{preview_id}.jpg"
            preview_path = os.path.join(Config.PREVIEW_FOLDER, preview_filename)

            # 使用视频处理器生成预览
            replacer = VideoBackgroundReplacer(preview_id)
            success = replacer.generate_preview_image(
                human_file_path, bg_file_path, params, preview_path
            )

            if success:
                # 记录活跃预览
                self.active_previews[preview_id] = {
                    'path': preview_path,
                    'created_time': datetime.now()
                }
                return preview_path

            return None

        except Exception as e:
            logger.error(f"创建预览失败: {e}")
            return None

    def get_preview_path(self, preview_id):
        """获取预览路径"""
        if preview_id in self.active_previews:
            return self.active_previews[preview_id]['path']

        # 尝试从文件系统查找
        preview_filename = f"preview_{preview_id}.jpg"
        preview_path = os.path.join(Config.PREVIEW_FOLDER, preview_filename)

        if os.path.exists(preview_path):
            return preview_path

        return None

    def delete_preview(self, preview_id):
        """删除预览"""
        try:
            # 从活跃预览中移除
            if preview_id in self.active_previews:
                preview_info = self.active_previews.pop(preview_id)
                preview_path = preview_info['path']
            else:
                preview_path = os.path.join(Config.PREVIEW_FOLDER, f"preview_{preview_id}.jpg")

            # 删除文件
            if os.path.exists(preview_path):
                os.remove(preview_path)
                logger.info(f"已删除预览文件: {preview_path}")
                return True

            return False

        except Exception as e:
            logger.error(f"删除预览失败: {e}")
            return False

    def cleanup_old_previews(self, hours=None):
        """清理旧的预览文件"""
        if hours is None:
            hours = Config.PREVIEW_RETENTION_HOURS

        # 如果hours为0，表示清理所有文件
        if hours == 0:
            cutoff_time = datetime.now() + timedelta(days=1)  # 未来时间，确保清理所有文件
        else:
            cutoff_time = datetime.now() - timedelta(hours=hours)

        cleaned_count = 0

        logger.info(f"开始清理预览文件，保留时间: {hours}小时，截止时间: {cutoff_time}")

        try:
            # 清理活跃预览中的过期项
            expired_previews = []
            for preview_id, info in self.active_previews.items():
                if info['created_time'] < cutoff_time:
                    expired_previews.append(preview_id)

            for preview_id in expired_previews:
                if self.delete_preview(preview_id):
                    cleaned_count += 1

            # 清理文件系统中的过期预览
            if os.path.exists(Config.PREVIEW_FOLDER):
                for item in os.listdir(Config.PREVIEW_FOLDER):
                    if item.startswith('preview_') and item.endswith('.jpg'):
                        item_path = os.path.join(Config.PREVIEW_FOLDER, item)
                        try:
                            item_time = datetime.fromtimestamp(os.path.getctime(item_path))
                            if item_time < cutoff_time:
                                os.remove(item_path)
                                cleaned_count += 1
                                logger.info(f"已清理过期预览文件: {item_path}")
                        except Exception as e:
                            logger.error(f"清理预览文件失败 {item_path}: {e}")

            logger.info(f"预览清理完成，清理了 {cleaned_count} 个文件")
            return cleaned_count

        except Exception as e:
            logger.error(f"清理预览失败: {e}", exc_info=True)
            return 0


class CleanupManager:
    """清理管理器"""

    @staticmethod
    def cleanup_temp_files(hours=None):
        """清理临时文件"""
        if hours is None:
            hours = Config.TEMP_RETENTION_HOURS

        # 如果hours为0，表示清理所有文件
        if hours == 0:
            cutoff_time = datetime.now() + timedelta(days=1)  # 未来时间
        else:
            cutoff_time = datetime.now() - timedelta(hours=hours)

        cleaned_count = 0

        logger.info(f"开始清理临时文件，保留时间: {hours}小时，截止时间: {cutoff_time}")

        try:
            # 清理临时目录
            if os.path.exists(Config.TEMP_FOLDER):
                for item in os.listdir(Config.TEMP_FOLDER):
                    item_path = os.path.join(Config.TEMP_FOLDER, item)
                    if os.path.isdir(item_path):
                        try:
                            item_time = datetime.fromtimestamp(os.path.getctime(item_path))
                            if item_time < cutoff_time:
                                shutil.rmtree(item_path)
                                cleaned_count += 1
                                logger.info(f"已清理临时目录: {item_path}")
                        except Exception as e:
                            logger.error(f"清理临时目录失败 {item_path}: {e}")

            logger.info(f"临时文件清理完成，清理了 {cleaned_count} 个目录")
            return cleaned_count

        except Exception as e:
            logger.error(f"清理临时文件失败: {e}", exc_info=True)
            return 0

    @staticmethod
    def cleanup_output_files(days=7):
        """清理输出文件"""
        # 如果days为0，表示清理所有文件
        if days == 0:
            cutoff_time = datetime.now() + timedelta(days=1)  # 未来时间
        else:
            cutoff_time = datetime.now() - timedelta(days=days)

        cleaned_count = 0

        logger.info(f"开始清理输出文件，保留时间: {days}天，截止时间: {cutoff_time}")

        try:
            if os.path.exists(Config.OUTPUT_FOLDER):
                for item in os.listdir(Config.OUTPUT_FOLDER):
                    item_path = os.path.join(Config.OUTPUT_FOLDER, item)
                    try:
                        item_time = datetime.fromtimestamp(os.path.getctime(item_path))
                        if item_time < cutoff_time:
                            os.remove(item_path)
                            cleaned_count += 1
                            logger.info(f"已清理输出文件: {item_path}")
                    except Exception as e:
                        logger.error(f"清理输出文件失败 {item_path}: {e}")

            logger.info(f"输出文件清理完成，清理了 {cleaned_count} 个文件")
            return cleaned_count

        except Exception as e:
            logger.error(f"清理输出文件失败: {e}", exc_info=True)
            return 0

    @staticmethod
    def cleanup_chunks(hours=2):
        """清理分片文件"""
        file_manager = FileManager()
        return file_manager.cleanup_chunks(hours)

    @staticmethod
    def full_cleanup():
        """完整清理"""
        logger.info("开始完整清理...")

        preview_manager = PreviewManager()

        results = {
            'previews': preview_manager.cleanup_old_previews(),
            'temp_files': CleanupManager.cleanup_temp_files(),
            'output_files': CleanupManager.cleanup_output_files(),
            'chunks': CleanupManager.cleanup_chunks()
        }

        total_cleaned = sum(results.values())
        logger.info(f"完整清理完成，总共清理了 {total_cleaned} 个项目")

        return results